// Copyright (c) Andrew Knight
// This software is licensed under the MIT: http://www.opensource.org/licenses/MIT

// Based on code from http://code.msdn.microsoft.com/windowsapps/Direct2D-Basic-Animation-d0a5a4d1, original license:
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved

#include "QtClient.h"

using namespace Microsoft::WRL;
using namespace D2D1;
using namespace Windows::Foundation;
using namespace Windows::UI::Core;
using namespace Windows::UI::ViewManagement;
using namespace Windows::System;
using namespace Platform;

#include <QtGui/QMouseEvent>
#include <QtCore/QDebug>

QtClient::QtClient(MetroApplication* metroApp)
{
	this->metroApp = metroApp;
}

void QtClient::Initialize(Windows::UI::Core::CoreWindow^ window, float dpi)
{
	DirectXBase::Initialize(window, dpi);
	m_window->PointerPressed += 
		ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &QtClient::OnPointerPressed);

    m_window->PointerMoved += 
		ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &QtClient::OnPointerMoved);

    m_window->PointerReleased += 
		ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &QtClient::OnPointerReleased);

	m_window->PointerWheelChanged +=
		ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &QtClient::OnPointerWheelChanged);

	ApplicationLayout::GetForCurrentView()->LayoutChanged +=
		ref new TypedEventHandler<ApplicationLayout^, ApplicationLayoutChangedEventArgs^>(this, &QtClient::OnLayoutChanged);
}

QMouseEvent QtClient::PointerArgsToMouseEvent(QEvent::Type type, Windows::UI::Core::PointerEventArgs^ args) {
	Windows::UI::Input::PointerPointProperties^ props = args->CurrentPoint->Properties;
	return QMouseEvent(type,
		QPoint(static_cast<int>(args->CurrentPoint->Position.X), static_cast<int>(args->CurrentPoint->Position.Y)),
		props->IsLeftButtonPressed ? Qt::LeftButton :
		(props->IsRightButtonPressed ? Qt::RightButton :
		(props->IsMiddleButtonPressed ? Qt::MiddleButton : Qt::NoButton)),
		(props->IsLeftButtonPressed ? Qt::LeftButton : Qt::NoButton) |
		(props->IsRightButtonPressed ? Qt::RightButton : Qt::NoButton) |
		(props->IsMiddleButtonPressed ? Qt::MiddleButton : Qt::NoButton),
		((args->KeyModifiers & VirtualKeyModifiers::Control) != VirtualKeyModifiers::None ? Qt::ControlModifier : Qt::NoModifier) |
		((args->KeyModifiers & VirtualKeyModifiers::Menu) != VirtualKeyModifiers::None ? Qt::AltModifier : Qt::NoModifier) |
		((args->KeyModifiers & VirtualKeyModifiers::Shift) != VirtualKeyModifiers::None ? Qt::ShiftModifier : Qt::NoModifier) |
		((args->KeyModifiers & VirtualKeyModifiers::Windows) != VirtualKeyModifiers::None ? Qt::MetaModifier : Qt::NoModifier)
		);
}

void QtClient::OnPointerPressed(_In_ Windows::UI::Core::CoreWindow^ sender, _In_ Windows::UI::Core::PointerEventArgs^ args)
{
	QApplication::sendEvent(metroApp, &PointerArgsToMouseEvent(QEvent::MouseButtonPress, args));
}

void QtClient::OnPointerMoved(_In_ Windows::UI::Core::CoreWindow^ sender, _In_ Windows::UI::Core::PointerEventArgs^ args)
{
	QApplication::sendEvent(metroApp, &PointerArgsToMouseEvent(QEvent::MouseMove, args));
}

void QtClient::OnPointerReleased(_In_ Windows::UI::Core::CoreWindow^ sender, _In_ Windows::UI::Core::PointerEventArgs^ args)
{
	QApplication::sendEvent(metroApp, &PointerArgsToMouseEvent(QEvent::MouseButtonRelease, args));
}

void QtClient::OnPointerWheelChanged(_In_ Windows::UI::Core::CoreWindow^ sender, _In_ Windows::UI::Core::PointerEventArgs^ args)
{
	QWheelEvent event(QPoint(static_cast<int>(args->CurrentPoint->Position.X), static_cast<int>(args->CurrentPoint->Position.Y)),
		args->CurrentPoint->Properties->MouseWheelDelta, Qt::NoButton, Qt::NoModifier);
	QApplication::sendEvent(metroApp, &event);
}

void QtClient::OnLayoutChanged(_In_ Windows::UI::ViewManagement::ApplicationLayout^ sender, _In_ Windows::UI::ViewManagement::ApplicationLayoutChangedEventArgs^ args)
{
	switch(args->Layout) {
	case ApplicationLayoutState::FullScreen:
		metroApp->changeLayout(MetroApplication::Fullscreen);
		break;
	case ApplicationLayoutState::Filled:
		metroApp->changeLayout(MetroApplication::Filled);
		break;
	case ApplicationLayoutState::Snapped:
		metroApp->changeLayout(MetroApplication::Snapped);
		break;
	}
}

void QtClient::CreateDeviceResources()
{
    DirectXBase::CreateDeviceResources();
}

void QtClient::CreateWindowSizeDependentResources()
{
    DirectXBase::CreateWindowSizeDependentResources();
	m_d2dContext->CreateBitmap(
        SizeU(m_windowBounds.Width, m_windowBounds.Height),
        BitmapProperties(
			PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)
        ),
        &m_bitmap
		);
	metroApp->resize(QSize(static_cast<int>(m_windowBounds.Width), static_cast<int>(m_windowBounds.Height)));
}

void QtClient::Render()
{
	metroApp->render();
    m_d2dContext->BeginDraw();
	m_bitmap.Get()->CopyFromMemory(&RectU(0, 0, m_windowBounds.Width, m_windowBounds.Height), metroApp->image().constBits(), 4 * m_windowBounds.Width);
    m_d2dContext->DrawBitmap(m_bitmap.Get());
	HRESULT hr = m_d2dContext->EndDraw();

    if (hr == D2DERR_RECREATE_TARGET)
    {
        m_d2dContext->SetTarget(nullptr);
        m_d2dTargetBitmap = nullptr;
        CreateDeviceResources();
        CreateWindowSizeDependentResources();
    }
    else
    {
        DX::ThrowIfFailed(hr);
    }

    Present();
}